/**
 * @file desktop.c
 *
 */

/*********************
 *      INCLUDES
 *********************/
#include "lv_watch.h"

#if USE_LV_WATCH_DESKTOP != 0

#include <stdio.h>

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/
#define DESK_TOP_VER_DRAG_START_THRESHOLD 1//10

/**********************
 *  STATIC PROTOTYPES
 **********************/
static lv_res_t desktop_tabpage_scrl_signal(lv_obj_t * tab_scrl, lv_signal_t sign, void * param);
static lv_res_t desktop_tabpage_signal(lv_obj_t * tab_page, lv_signal_t sign, void * param);
static void tabpage_pressed_handler(lv_obj_t * tabview, lv_obj_t * tabpage);
static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage);
static void tabpage_press_lost_handler(lv_obj_t * tabview, lv_obj_t * tabpage);
static lv_res_t desktop_child_tab_hide_action(lv_coord_t new_y);
static void desktop_child_tab_hide_anim_end(lv_obj_t * child_tab);
static void desktop_disable_parent_sliding(lv_obj_t * tab_scrl);
static void desktop_recover_parent_sliding(lv_obj_t * tab_scrl);
static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param);
static void desktop_child_tab_full_shown_anim_end(lv_obj_t * child_tab);

/**********************
 *  STATIC VARIABLES
 **********************/
static lv_signal_cb_t ancient_page_signal;
static lv_signal_cb_t ancient_page_scrl_signal;
static lv_obj_t * desktop_child_tab;
static bool desktop_par_origin_sliding_en;
static bool desktop_par_scrl_origin_drag_en;
static bool desktop_par_sliding_en_need_recover;
static bool desktop_child_is_up;
static lv_obj_t * desktop_tab;

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
***********************/
lv_obj_t * desktop_create(lv_obj_t * tabview)
{
    lv_obj_t * tab = lv_tabview_add_tab(tabview, "");
    lv_mem_assert(tab);
    if(tab == NULL) return NULL;

    lv_page_set_sb_mode(tab, LV_SB_MODE_OFF);

    if(ancient_page_signal == NULL) ancient_page_signal = lv_obj_get_signal_func(tab);
    lv_obj_set_signal_cb(tab, desktop_tabpage_signal);

    if(ancient_page_scrl_signal == NULL) ancient_page_scrl_signal = lv_obj_get_signal_func(lv_page_get_scrl(tab));
    lv_obj_set_signal_cb(lv_page_get_scrl(tab), desktop_tabpage_scrl_signal);

    desktop_tab = tab;

    return tab;
}

void desktop_add_element(lv_obj_t * element)
{
    lv_obj_set_free_ptr(element, lv_obj_get_signal_func(element));
    lv_obj_set_signal_cb(element, element_signal_func);
    lv_obj_set_protect(element, LV_PROTECT_PRESS_LOST | LV_PROTECT_CLICK_FOCUS);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static lv_res_t element_signal_func(lv_obj_t * element, lv_signal_t sign, void * param)
{
    lv_res_t res;

    //printf("element_signal_func, sign=%d\n", sign);
    /* if not click signs, invoke ancient handler and return */
    if(sign != LV_SIGNAL_PRESSING && sign != LV_SIGNAL_PRESSED && sign != LV_SIGNAL_RELEASED && sign != LV_SIGNAL_PRESS_LOST) {
        /* Include the ancient signal function */
        lv_signal_cb_t sign_func = lv_obj_get_free_ptr(element);
        res = sign_func(element, sign, param);
        if(res != LV_RES_OK) return res;

        return LV_RES_OK;
    }

    lv_obj_t * tab_page = lv_obj_get_parent(element);
    while(tab_page) {
        if(lv_obj_get_signal_func(tab_page) != desktop_tabpage_signal) tab_page = lv_obj_get_parent(tab_page);
        else break;
    }
    if(tab_page == NULL) return LV_RES_INV;

    desktop_tabpage_signal(tab_page, sign, param);

    lv_obj_t * cont = lv_obj_get_parent(tab_page);
    lv_obj_t * tabview = lv_obj_get_parent(cont);
    lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
    if(!ext->element_click_en || desktop_child_tab != NULL) {
        return LV_RES_OK;
    }

    /* Include the ancient signal function */
    lv_signal_cb_t sign_func = lv_obj_get_free_ptr(element);
    res = sign_func(element, sign, param);
    if(res != LV_RES_OK) return res;

    return LV_RES_OK;
}

/**
 * Signal function of a tab's page
 * @param tab pointer to a tab page object
 * @param sign a signal type from lv_signal_t enum
 * @param param pointer to a signal specific variable
 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
 */
static lv_res_t desktop_tabpage_signal(lv_obj_t * tab_page, lv_signal_t sign, void * param)
{
    lv_res_t res = LV_RES_OK;

    //printf("desktop_tabpage_signal: sign =%d\n", sign);
    if(sign != LV_SIGNAL_PRESSED && sign != LV_SIGNAL_PRESSING && sign != LV_SIGNAL_RELEASED &&  sign != LV_SIGNAL_PRESS_LOST) {
        /* Include the ancient signal function */
        res = ancient_page_signal(tab_page, sign, param);
        if(res != LV_RES_OK) return res;
    } else {
        if(launcher_get_menu_switch_en()) {
            /* Include the ancient signal function */
            res = ancient_page_signal(tab_page, sign, param);
            if(res != LV_RES_OK) return res;
        }
    }

#if 1
    lv_obj_t * cont = lv_obj_get_parent(tab_page);
    lv_obj_t * tabview = lv_obj_get_parent(cont);

    if(sign == LV_SIGNAL_PRESSED) {
        tabpage_pressed_handler(tabview, tab_page);
    } else if(sign == LV_SIGNAL_PRESSING) {
        tabpage_pressing_handler(tabview, tab_page);
    } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
        tabpage_press_lost_handler(tabview, tab_page);
    }
#endif
    return res;
}


/**
 * Signal function of the tab page's scrollable object
 * @param tab_scrl pointer to a tab page's scrollable object
 * @param sign a signal type from lv_signal_t enum
 * @param param pointer to a signal specific variable
 * @return LV_RES_OK: the object is not deleted in the function; LV_RES_INV: the object is deleted
 */
static lv_res_t desktop_tabpage_scrl_signal(lv_obj_t * tab_scrl, lv_signal_t sign, void * param)
{
    lv_res_t res;

    //printf("desktop_tabpage_scrl_signal: sign =%d\n", sign);
    lv_obj_t * tab_page = lv_obj_get_parent(tab_scrl);
    lv_obj_t * cont = lv_obj_get_parent(tab_page);
    lv_obj_t * tabview = lv_obj_get_parent(cont);

    /* Include the ancient signal function */
    res = ancient_page_scrl_signal(tab_scrl, sign, param);
    if(res != LV_RES_OK) return res;

#if 1
    if(sign == LV_SIGNAL_PRESSED) {
        tabpage_pressed_handler(tabview, tab_page);
    } else if(sign == LV_SIGNAL_PRESSING) {
        tabpage_pressing_handler(tabview, tab_page);
    } else if(sign == LV_SIGNAL_RELEASED || sign == LV_SIGNAL_PRESS_LOST) {
        tabpage_press_lost_handler(tabview, tab_page);
    }
#endif

    return res;
}

/**
 * Called when a tab's page or scrollable object is pressed
 * @param tabview pointer to the btn view object
 * @param tabpage pointer to the page of a btn
 */
static void tabpage_pressed_handler(lv_obj_t * tabview, lv_obj_t * tabpage)
{
    (void)tabpage;

    lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
    lv_indev_t * indev = lv_indev_get_act();
    lv_indev_get_point(indev, &ext->point_last);

    /* desktop_child_tab is already delete by other UI */
    if(desktop_child_tab != lv_obj_get_child(tabview, NULL)) {
        desktop_child_tab = NULL;
        desktop_recover_parent_sliding(lv_page_get_scrl(desktop_tab));
    }
}

/**
 * Called when a tab's page or scrollable object is being pressed
 * @param tabview pointer to the btn view object
 * @param tabpage pointer to the page of a btn
 */
static void tabpage_pressing_handler(lv_obj_t * tabview, lv_obj_t * tabpage)
{
    lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t point_act;
    lv_indev_get_point(indev, &point_act);
    lv_coord_t y_diff = point_act.y - ext->point_last.y;

    if(ext->draging == 0) {
        if(y_diff >= DESK_TOP_VER_DRAG_START_THRESHOLD || y_diff <= -DESK_TOP_VER_DRAG_START_THRESHOLD) {
            ext->drag_hor = 0;
            ext->draging = 1;
            //printf("desktop: set element_click_en = 0\n");
            ext->element_click_en = 0;
            launcher_clean_hide_tabs(tabview);
        }
    }

    //ver drag
    if(ext->draging == 1 || ext->drag_hor == 0) {
        if(y_diff > DESK_TOP_VER_DRAG_START_THRESHOLD) {
            if(desktop_child_tab == NULL) {
#if USE_LV_WATCH_DISPLAY_SELECTION != 0
                //printf("creat display_selection_create\n");
                desktop_child_is_up = true;
                desktop_child_tab = display_selection_create(tabview, lv_page_get_scrl(desktop_tab), desktop_tabpage_scrl_signal, desktop_child_is_up);
                /* hidden, up position of desktop */
                lv_obj_set_y(desktop_child_tab, -lv_obj_get_height(tabview));
                desktop_disable_parent_sliding(lv_page_get_scrl(desktop_tab));

#endif
            }
        } else if(y_diff < -DESK_TOP_VER_DRAG_START_THRESHOLD) {
            if(
                desktop_child_tab == NULL) {
#if USE_LV_WATCH_DISPLAY_SELECTION != 0
                //printf("creat simple_setting_create\n");
                desktop_child_is_up = false;
                desktop_child_tab = display_selection_create(tabview, lv_page_get_scrl(desktop_tab), desktop_tabpage_scrl_signal, desktop_child_is_up);
                lv_obj_set_y(desktop_child_tab, lv_obj_get_height(tabview));
                desktop_disable_parent_sliding(lv_page_get_scrl(desktop_tab));

#endif
            }
        }

        if(desktop_child_tab) {
            lv_coord_t new_y = lv_obj_get_y(desktop_child_tab) + y_diff;
            if((desktop_child_is_up && new_y > 0) || (!desktop_child_is_up && new_y < 0)) {
                /* full shown */
                new_y = 0;
            }
            //printf("lv_obj_set_y: new_y=%d\n", new_y);
            lv_obj_set_y(desktop_child_tab, new_y);
        }

        ext->point_last.x = point_act.x;
        ext->point_last.y = point_act.y;
    }

    if(desktop_child_tab == NULL) {
        /* hor drag */
        if(launcher_get_menu_switch_en()) {
            /* only switch once base on press lost handler */
            if(launcher_handle_switch_tab(tabview, tabpage)) {
                launcher_set_menu_switch_en(false);
            }
        }
    }
}

/**
 * Called when a tab's page or scrollable object is released or the press id lost
 * @param tabview pointer to the btn view object
 * @param tabpage pointer to the page of a btn
 */
static void tabpage_press_lost_handler(lv_obj_t * tabview, lv_obj_t * tabpage)
{
    lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
    ext->drag_hor = 0;
    ext->draging = 0;

    lv_obj_set_drag(lv_page_get_scrl(tabpage), true);
    launcher_set_menu_switch_en(true);

    lv_indev_t * indev = lv_indev_get_act();
    lv_point_t point_act;
    lv_indev_get_point(indev, &point_act);

    lv_point_t vect;
    lv_indev_get_vect(indev, &vect);

    if(desktop_child_tab != NULL) {
        /* ver drag */
        lv_coord_t y = lv_obj_get_y(desktop_child_tab);
        lv_coord_t y_predict = 0;

        while(vect.y != 0)   {
            y_predict += vect.y;
            vect.y = vect.y * (100 - LV_INDEV_DEF_DRAG_THROW) / 100;
        }

        lv_coord_t y1 = y - tabview->coords.y1 + y_predict;
        lv_coord_t y2 = y1 + lv_obj_get_height(desktop_child_tab);
        lv_coord_t treshold = lv_obj_get_height(tabview) / 2;
        //printf("desktop: y=%d, y1=%d, y_predict=%d, y2=%d\n", y, y1, y_predict, y2);

        if((desktop_child_is_up && vect.y > 0) || (!desktop_child_is_up && vect.y < 0) ||
                (desktop_child_is_up && y2 > treshold) || (!desktop_child_is_up && y1 < treshold)) {
            /* full shown */
            //printf("desktop: full shown.\n");
            //lv_obj_set_y(desktop_child_tab, 0);
            lv_obj_set_y_animation(desktop_child_tab, 0, true, LV_WATCH_ANIM_TIME, desktop_child_tab_full_shown_anim_end);
        } else if(y != 0) {
            lv_coord_t new_y = (y > 0) ? lv_obj_get_height(tabview) : -lv_obj_get_height(tabview);
            //lv_obj_set_y(desktop_child_tab, new_y);
            desktop_child_tab_hide_action(new_y);
        }
    }
}

static lv_res_t desktop_child_tab_hide_action(lv_coord_t new_y)
{
#if USE_LV_ANIMATION
    lv_obj_set_y_animation(desktop_child_tab, new_y, true, LV_WATCH_ANIM_TIME, desktop_child_tab_hide_anim_end);
    return LV_RES_OK;
#else
    /* cleanup child tab */
    //printf("desktop: cleanup child tab at once.\n");
    desktop_child_tab_hide_anim_end(NULL);
    return LV_RES_INV;
#endif
}

static void desktop_child_tab_hide_anim_end(lv_anim_t * a)
{
    (void)a;
    /* cleanup child tab */
    //printf("desktop: cleanup child tab.\n");
    lv_obj_del(desktop_child_tab);
    desktop_child_tab = NULL;
    desktop_recover_parent_sliding(lv_page_get_scrl(desktop_tab));

    lv_obj_t * cont = lv_obj_get_parent(desktop_tab);
    lv_obj_t * tabview = lv_obj_get_parent(cont);
    lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
    //printf("desktop set ext->element_click_en = 1\n");
    ext->element_click_en = 1;
    launcher_create_hide_tabs(tabview);
}

static void desktop_child_tab_full_shown_anim_end(lv_anim_t * a)
{
    (void)a;

    lv_obj_t * cont = lv_obj_get_parent(desktop_tab);
    lv_obj_t * tabview = lv_obj_get_parent(cont);
    lv_tabview_ext_t * ext = lv_obj_get_ext_attr(tabview);
    //printf("desktop set ext->element_click_en = 1\n");
    ext->element_click_en = 1;
}

static void desktop_disable_parent_sliding(lv_obj_t * tab_scrl)
{
    lv_obj_t * tab_page = lv_obj_get_parent(tab_scrl);
    lv_obj_t * cont = lv_obj_get_parent(tab_page);
    lv_obj_t * tabview = lv_obj_get_parent(cont);

    //printf("disable parent sliding\n");
    desktop_par_origin_sliding_en = lv_tabview_get_sliding(tabview);
    desktop_par_scrl_origin_drag_en = lv_obj_get_drag(tab_scrl);
    lv_tabview_set_sliding(tabview, false);
    lv_obj_set_drag(tab_scrl, false);
    desktop_par_sliding_en_need_recover = true;
}

static void desktop_recover_parent_sliding(lv_obj_t * tab_scrl)
{
    lv_obj_t * tab_page = lv_obj_get_parent(tab_scrl);
    lv_obj_t * cont = lv_obj_get_parent(tab_page);
    lv_obj_t * tabview = lv_obj_get_parent(cont);

    if(desktop_par_sliding_en_need_recover) {
        //printf("reover desktop_par_origin_sliding_en=%d\n", desktop_par_origin_sliding_en);
        lv_tabview_set_sliding(tabview, desktop_par_origin_sliding_en);
        lv_obj_set_drag(tab_scrl, desktop_par_scrl_origin_drag_en);
    }
    desktop_par_sliding_en_need_recover = false;
}

#endif /*USE_LV_WATCH_DESKTOP*/
